#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef WIN32
#include <io.h>
#endif
#include "n2base.h"
#include "Dumper.h"

#define DUMP_AS_AUTO            0
#define DUMP_AS_EXE             1
#define DUMP_AS_MSCOFF          2
#define DUMP_AS_ELF             3
#define DUMP_AS_MACH_O          4
#define DUMP_AS_RES             5

static void DumperShowHelp(void);

static int format = DUMP_AS_AUTO;
bool dumpRaw = false;
bool macIsLittle = true;
bool fileIsLittle = true;

#ifdef WIN32

int wmain(int argc, wchar_t ** argv)
{
    int i;
    if (libn2base_init() < 0)
        return -1;
    dumper_init_multilanguage();

    if (argc==1) {
        DumperShowHelp();
        exit(0);
    }

    if (is_little_endian())
        macIsLittle = true;
    else
        macIsLittle = false;

    for (i = 1; i < argc; i++) {
        if (argv[i][0] == L'-' || argv[i][0] == L'/')
        {
            if (0 == wcscmp(&argv[i][1], L"f")) 
            {
                if (i+1 >= argc) {
                    n2print("%s : %s\n", dpstr(S_n2dumper_Error), dpstr(S_n2dumper_too_few_param));
                    exit(-1);
                } 
                else {
                    i++;
                    if (0 == wcscmp(argv[i], L"exe"))
                        format = DUMP_AS_EXE;
                    else if (0 == wcscmp(argv[i], L"mscoff"))
                        format = DUMP_AS_MSCOFF;
                    else if (0 == wcscmp(argv[i], L"elf"))
                        format = DUMP_AS_ELF;
                    else if (0 == wcscmp(argv[i], L"macho"))
                        format = DUMP_AS_MACH_O;
                    else if (0 == wcscmp(argv[i], L"res"))
                        format = DUMP_AS_RES;
                    else {
                        n2print("%s : %s\n", dpstr(S_n2dumper_Error), dpstr(S_n2dumper_unknown_format));
                        exit(-1);
                    }

                }
            }
            else if (0 == wcscmp(&argv[i][1], L"raw")) 
                dumpRaw = true;
            else 
            {
                n2print("%s : %s %s\n", dpstr(S_n2dumper_Error), dpstr(S_n2dumper_not_supported_option), 
                    dp_u16stou8((unsigned short*)argv[i]));
            }
        }
        else
        {
            FILE * fp;
            fp = _wfopen(argv[i], L"rb");
            if (fp == NULL) {
                n2print("%s : %s \"%s\"\n", dpstr(S_n2dumper_Error), dpstr(S_n2dumper_cannot_open_file), 
                    dp_u16stou8((unsigned short*)argv[i]));
            }

            try
            {
                switch (format)
                {
                case DUMP_AS_EXE:
                    dump_exe_file(fp, dp_u16stou8((unsigned short*)argv[i]));
                    break;
                case DUMP_AS_MSCOFF:
                    dump_msobj_file(fp, dp_u16stou8((unsigned short*)argv[i]));
                    break;
                case DUMP_AS_ELF:
                    dump_elf_file(fp, dp_u16stou8((unsigned short*)argv[i]));
                    break;
                case DUMP_AS_MACH_O:
                    dump_macho_file(fp, dp_u16stou8((unsigned short*)argv[i]));
                    break;
                case DUMP_AS_RES:
                    dump_res_file(fp, dp_u16stou8((unsigned short*)argv[i]));
                    break;
                default:
                    dump_unknow_file(fp, dp_u16stou8((unsigned short*)argv[i]));
                    break;
                }
            }
            catch (enum DpEx e)
            {
            	
            }
            
            n2print("\n\n\n");
            fclose(fp);
        }

    }
}

#else

int main(int argc, char** argv)
{

}

#endif  //WIN32

static void DumperShowHelp(void)
{
    n2print(dpstr(S_n2dumper_logo_string));
    n2print(dpstr(S_n2dumper_usage_text_1));
    n2print(dpstr(S_n2dumper_usage_text_2));
    n2print(dpstr(S_n2dumper_usage_text_3));
    n2print(dpstr(S_n2dumper_usage_text_4));

    n2print(dpstr(S_n2dumper_usage_text_5));
    n2print(dpstr(S_n2dumper_usage_text_6));
    n2print(dpstr(S_n2dumper_usage_text_7));
    n2print(dpstr(S_n2dumper_usage_text_8));
    n2print(dpstr(S_n2dumper_usage_text_9));
}

#define U8BUFF_TEMP_SIZE        (64*1024)
static char u8convstr[U8BUFF_TEMP_SIZE];

static void U32CharToU8Buff(unsigned u32char, unsigned char* pbUtf8Buff, int* pUtf8len, int* pValid);

char* dp_u16stou8(const unsigned short* pwStr)
{
    char sbuff[8];
    int u8len, valid, i;
    int bcount = 0;
    int wcount = 0;
    while (1) {
        U32CharToU8Buff((unsigned)pwStr[wcount], (unsigned char*)sbuff, &u8len, &valid);
        if (!valid) {
            sbuff[0] = '?';
            u8len = 1;
        }
        if (bcount+u8len < U8BUFF_TEMP_SIZE-1) {
            for (i = 0; i < u8len; i++)
                u8convstr[bcount+i] = sbuff[i];
        } else {
            u8convstr[bcount] = 0;
            break;
        }
        bcount += u8len;
        wcount ++;
        if (pwStr[wcount-1]==0)
            break;
    }
    return u8convstr;
}

char* dp_u32stou8(const unsigned * pdStr)
{
    char sbuff[8];
    int u8len, valid, i;
    int bcount = 0;
    int dcount = 0;
    while (1) {
        U32CharToU8Buff(pdStr[dcount], (unsigned char*)sbuff, &u8len, &valid);
        if (!valid) {
            sbuff[0] = '?';
            u8len = 1;
        }
        if (bcount+u8len < U8BUFF_TEMP_SIZE-1) {
            for (i = 0; i < u8len; i++)
                u8convstr[bcount+i] = sbuff[i];
        } else {
            u8convstr[bcount] = 0;
            break;
        }
        bcount += u8len;
        dcount ++;
        if (pdStr[dcount-1]==0)
            break;
    }
    return u8convstr;
}


static void U32CharToU8Buff(unsigned u32char, unsigned char* pbUtf8Buff, int* pUtf8len, int* pValid)
{
    if ( u32char < 0x80 )               // 7-bit
    {
        *pbUtf8Buff = (unsigned char)u32char;
        *pUtf8len = 1;
        *pValid = 1;
    }
    else if ( u32char < 0x800 )         // 11-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>6)|0xC0);
        *(pbUtf8Buff+1) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 2;
        *pValid = 1;
    }
    else if ( u32char < 0x10000 )       // 16-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>12)|0xE0);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 3;
        *pValid = 1;
    }
    else if ( u32char < 0x200000 )      // 21-bit
    {
        *pbUtf8Buff = (unsigned char) ((u32char>>18)|0xF0);
        *(pbUtf8Buff+1) = (unsigned char) ((u32char >> 12) & 0x3F | 0x80);
        *(pbUtf8Buff+2) = (unsigned char) ((u32char >> 6) & 0x3F | 0x80);
        *(pbUtf8Buff+3) = (unsigned char) (u32char & 0x3F | 0x80);
        *pUtf8len = 4;
        *pValid = 1;
    }
    else
    {
        *pbUtf8Buff = (unsigned char)u32char;
        *pUtf8len = 1;
        *pValid = 0;
    }
}

/////////////////////////////////////////////////////////////////////////////////////
// swap endian funtion
/////////////////////////////////////////////////////////////////////////////////////
unsigned short dpSwap16(unsigned short value)
{
    return (((value&0xff)<<8) | ((value>>8)&0xff));
}

unsigned dpSwap32(unsigned value)
{
    return (((value&0xff)<<24) | (((value>>8)&0xff)<<16) | (((value>>16)&0xff)<<8) | ((value>>24)&0xff));
}

unsigned long long dpSwap64(unsigned long long value)
{
    unsigned long long c1, c2, c3, c4, c5, c6, c7, c8, c0;
    c1 = value & 0xff;
    c2 = (value >> 8) & 0xff;
    c3 = (value >> 16) & 0xff;
    c4 = (value >> 24) & 0xff;
    c5 = (value >> 32) & 0xff;
    c6 = (value >> 40) & 0xff;
    c7 = (value >> 48) & 0xff;
    c8 = (value >> 56) & 0xff;
    c0 = ((c1<<56)|(c2<<48)|(c3<<40)|(c4<<32)|(c5<<24)|(c6<<16)|(c7<<8)|c8);
    return c0;
}

unsigned short dpAutoSwap16(unsigned short value)
{
    if (macIsLittle == fileIsLittle)
        return value;
    else
        return dpSwap16(value);
}

unsigned dpAutoSwap32(unsigned value)
{
    if (macIsLittle == fileIsLittle)
        return value;
    else
        return dpSwap32(value);
}

unsigned long long dpAutoSwap64(unsigned long long value)
{
    if (macIsLittle == fileIsLittle)
        return value;
    else
        return dpSwap64(value);
}                                                                                                                                                                                                               

static char i8tohexc(unsigned char c)
{
    if (c < 10)
        return c+'0';
    else if (c < 16)
        return c+'a'-10;
    else
        return '0';
}

static char i8toHexc(unsigned char c)
{
    if (c < 10)
        return c+'0';
    else if(c < 16)
        return c+'A'-10;
    else
        return '0';
}

const char* dpll2hex(unsigned long long iv)
{
    int i, bit;
    unsigned char c;
    static char hexstr[20];
    bit = 60;
    for (i = 0; i < 16; i++)
    {
        c = (unsigned char) ((iv >> bit) & 0x0f);
        hexstr[i] = i8tohexc(c);
        bit -= 4;
    }
    hexstr[i] = 0;
    return hexstr;
}

const char* dpll2Hex(unsigned long long iv)
{
    int i, bit;
    unsigned char c;
    static char hexstr[20];
    bit = 60;
    for (i = 0; i < 16; i++)
    {
        c = (unsigned char) ((iv >> bit) & 0x0f);
        hexstr[i] = i8toHexc(c);
        bit -= 4;
    }
    hexstr[i] = 0;
    return hexstr;
}

/////////////////////////////////////////////////////////////////////////////////////
// File I/O function
/////////////////////////////////////////////////////////////////////////////////////

static long long lDumppingFileSize;
static long long lDumpPartStart;
static long long lDumpPartSize;
static const char * pbDumppingFileName;

long long dpGetFileSize(FILE * fp) 
{
    return _filelength(_fileno(fp));
}

size_t dp_fread(void *buffer, size_t size, size_t count, FILE *stream)
{
    return fread(buffer, size, count, stream);
}

int dp_fseek64(FILE *stream, long long offset, int origin)
{
    return _fseeki64(stream, offset, origin);
}

int dp_fseek64_in_filepart(FILE *stream, long long offset, int origin)
{
    return dp_fseek64(stream, offset + lDumpPartStart, origin);
}

void dpSetDumppingFileSize(long long filesize)
{
    lDumppingFileSize = filesize;
}

void dpSetDumppingFileName(const char* filename)
{
    pbDumppingFileName = filename;
}

const char* dpGetDumppingFileName(void)
{
    return pbDumppingFileName;
}

void dpSetDumppingFilePart(long long start, long long size)
{
    lDumpPartStart = start;
    lDumpPartSize = size;
}

void dpSetFileFormatLittleEndian(bool isLittleEndian)
{
    fileIsLittle = isLittleEndian;
}












